<!--
Copyright (c) 2024 Industrial Shields. All rights reserved

This file is part of node-red-librpiplc.

node-red-librpiplc is free software: you can redistribute
it and/or modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation, either version
3 of the License, or (at your option) any later version.

node-red-librpiplc is distributed in the hope that it will
be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-->

<script type="text/javascript">
	const rpiplcAnalogOutputsAnalog0 = [ "Q0.0", "Q0.1", "Q0.2", "Q0.3", "Q0.4", "Q0.5", "Q0.6", "Q0.7", "A0.5", "A0.6", "A0.7", ];
	const rpiplcAnalogOutputsAnalog1 = [ "Q1.0", "Q1.1", "Q1.2", "Q1.3", "Q1.4", "Q1.5", "Q1.6", "Q1.7", "A1.5", "A1.6", "A1.7", ];
	const rpiplcAnalogOutputsAnalog2 = [ "Q2.0", "Q2.1", "Q2.2", "Q2.3", "Q2.4", "Q2.5", "Q2.6", "Q2.7", "A2.5", "A2.6", "A2.7", ];
	const rpiplcAnalogOutputsRelay0 = [ "Q0.0", "Q0.1", "Q0.2", "A0.0", "A0.1", "A0.2", ];
	const rpiplcAnalogOutputsRelay1 = [ "Q1.0", "Q1.1", "Q1.2", "A1.0", "A1.1", "A1.2", ];
	const rpiplcAnalogOutputsRelay2 = [ "Q2.0", "Q2.1", "Q2.2", "A2.0", "A2.1", "A2.2", ];

	const rpiplcV6ExtraAnalogOutputs = [];
	const rpiplcV6AnalogOutputPins = {
		"RPIPLC_19R": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0],
		"RPIPLC_21": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0],
		"RPIPLC_38AR": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsRelay1],
		"RPIPLC_38R": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0, ...rpiplcAnalogOutputsRelay1],
		"RPIPLC_42": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsAnalog1],
		"RPIPLC_50RRA": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsAnalog2],
		"RPIPLC_53ARR": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsRelay2],
		"RPIPLC_54ARA": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsAnalog2],
		"RPIPLC_57AAR": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsAnalog1, ...rpiplcAnalogOutputsRelay2],
		"RPIPLC_57R": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsRelay2],
		"RPIPLC_58": [...rpiplcV6ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsAnalog1, ...rpiplcAnalogOutputsAnalog2]
	};

	const rpiplcV4ExtraAnalogOutputs = ["PWM1", "PWM2", "PWM3"];
	const rpiplcV4AnalogOutputPins = {
		"RPIPLC_19R": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0],
		"RPIPLC_21": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0],
		"RPIPLC_38AR": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsRelay1],
		"RPIPLC_38R": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0, ...rpiplcAnalogOutputsRelay1],
		"RPIPLC_42": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsAnalog1],
		"RPIPLC_50RRA": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsAnalog2],
		"RPIPLC_53ARR": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsRelay2],
		"RPIPLC_54ARA": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsAnalog2],
		"RPIPLC_57AAR": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsAnalog1, ...rpiplcAnalogOutputsRelay2],
		"RPIPLC_57R": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsRelay2],
		"RPIPLC_58": [...rpiplcV4ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsAnalog1, ...rpiplcAnalogOutputsAnalog2]
	};

	const rpiplcV3ExtraAnalogOutputs = [];
	const rpiplcV3AnalogOutputPins = {
		"RPIPLC_19R": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0],
		"RPIPLC_21": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0],
		"RPIPLC_38AR": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsRelay1],
		"RPIPLC_38R": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0, ...rpiplcAnalogOutputsRelay1],
		"RPIPLC_42": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsAnalog1],
		"RPIPLC_50RRA": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsAnalog2],
		"RPIPLC_53ARR": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsRelay2],
		"RPIPLC_54ARA": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsAnalog2],
		"RPIPLC_57AAR": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsAnalog1, ...rpiplcAnalogOutputsRelay2],
		"RPIPLC_57R": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsRelay0, ...rpiplcAnalogOutputsRelay1, ...rpiplcAnalogOutputsRelay2],
		"RPIPLC_58": [...rpiplcV3ExtraAnalogOutputs, ...rpiplcAnalogOutputsAnalog0, ...rpiplcAnalogOutputsAnalog1, ...rpiplcAnalogOutputsAnalog2]
	};

	const pinsMapAnalogOutputPins = {
		"RPIPLC_V6": rpiplcV6AnalogOutputPins,
		"RPIPLC_V4": rpiplcV4AnalogOutputPins,
		"RPIPLC_V3": rpiplcV3AnalogOutputPins,
		"UPSAFEPI_V6": {"UPSAFEPI": []},
		"GATEBERRY_V9": {"GATEBERRY": []},
		"TOUCHBERRY_PI_V1": {"TOUCHBERRY_PI": []},
	};

	RED.nodes.registerType("rpiplc-analog-write", {
		category: "Industrial Shields",
		color: "#C7E9C0",

		defaults: {
			rpiplc: { value: "", type: "rpiplc-config", required: true },
			pin: { value: "", required: true },
			value: {
				value: null,
				validate: RED.validators.typedInput("valueType", false),
			},
			valueType: { value: "num" },
			name: { value: "" },
		},

		inputs: 1,
		outputs: 1,

		icon: "serial.svg",
		align: "left",
		labelStyle: "node_label",
		label: function() {
			if (this.name) {
				return this.name;
			}
			if (this.pin) {
				let pinStr = this.pin !== "CUSTOM" ? this.pin : "custom"
				if (this.value === '') {
					return `write ${pinStr}`;
				}
				return `write ${pinStr}: ${this.value}`;
			}
			return "invalid analog write";
		},
		paletteLabel: function() {
			return this.name || "analog write";
		},

		oneditprepare: function() {
			const analogWriteNode = this;

			$("#node-input-rpiplc").change(function() {
				const hideInputDiv = $("#hide-node-input-pin");
				const hideValueDiv = $("#hide-node-value-pin");
				const pinSelect = $("#node-input-pin");

				function hidePins() {
					hideInputDiv.show();
					hideInputDiv.hide();
					pinSelect.empty();
					pinSelect.append($("<option></option>").val(null).text("You found a bug!"));
				}

				const configId = $(this).val();
				if (!configId) {
					hidePins();
					return;
				}
				const configNode = RED.nodes.node(configId);
				if (!configNode) {
					hidePins();
					return;
				}

				const version = configNode.version;
				const model = configNode.model;
				if (version === "CUSTOM") {
					hidePins();
					pinSelect.append($("<option></option>").val("CUSTOM").text("CUSTOM"));
					pinSelect.val("CUSTOM");
					return;
				}

				const pinsArray = pinsMapAnalogOutputPins[version][model];
				if (pinsArray.length !== 0) {
					const actualValue = pinSelect.val() || analogWriteNode.pin;
					pinSelect.empty();
					pinsArray.forEach((pin) => {
						pinSelect.append($("<option></option>").val(pin).text(pin))
					});
					pinSelect.append($("<option></option>")
						.val("Message passed").text("Message passed"))
					// Replace if some pin was already set in the node
					if (pinsArray.includes(actualValue) || actualValue === "Message passed") {
						pinSelect.val(actualValue);
					}
					// Clean the error border if present
					if (pinSelect.hasClass("input-error")) {
						pinSelect.removeClass("input-error");
					}
					hideInputDiv.show();
					hideValueDiv.show();
				}
				else {
					hidePins();
				}
			});

			$("#node-input-value").typedInput({
				type: "num",
				types: ["num"],
				typeField: "#node-input-valueType",
			});
		},
	});
</script>

<script type="text/x-red" data-template-name="rpiplc-analog-write">
	<div class="form-row">
		<label for="node-input-rpiplc"><i class="fa fa-cog"></i> PLC type</label>
		<input type="text" id="node-input-rpiplc" placeholder="PLC type">
	</div>
	<div class="form-row" id="hide-node-input-pin">
		<label for="node-input-pin"><i class="fa fa-circle-o"></i> Output</label>
		<select id="node-input-pin" placeholder="Select an output"></select>
	</div>
	<div class="form-row" id="hide-node-input-value">
		<label for="node-input-value"><i class="fa fa-sliders"></i> Value</label>
		<input type="hidden" id="node-input-valueType">
		<input type="text" style="width:70%" id="node-input-value" placeholder="msg.payload">
	</div>
	<div class="form-row">
		<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
		<input type="text" id="node-input-name" placeholder="Name">
	</div>
</script>

<script type="text/x-red" data-help-name="rpiplc-analog-write">
  <p>
    <b>Analog Write</b>
  </p>
  <p>
    Writes an analog value in the specified pin. It returns a <code>msg.rc</code> attribute to check
    if the analog read was successful.
  </p>
  <p>
    If the selected pin is <code>Message passed</code> or if the configuration node is custom, this
    node will read the <code>msg.pin</code> attribute to determine which pin to read from.
  </p>
  <p>
    If the Value field is empty, this node will read the <code>msg.payload</code> attribute to
    determine which value will write to the pin. It must be an integer between 0 and 65535 (16-bit).
  </p>
  <p>
    This node (as well as the C library) has an accepted range of 16 bits. However, the maximum
    value must be adjusted depending on the PLC being used. For instance, the analog outputs of all
    Raspberry PLCs, from V3 to V6, operate up to 12 bits (i.e., 0 to 4095).
  </p>
  <p>
    See also the
    <a href="https://www.industrialshields.com/blog/raspberry-pi-for-industry-26/post/node-red-tutorial-how-to-set-analog-outputs-to-raspberry-plc-332">
    online documentation</a> for more information.
  </p>
</script>
